#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h> 
#include <errno.h>
#include <arpa/inet.h>
#include <sys/time.h>// gettimeofday
#include <zmq.h>

#include "setproctitle.h"
#include "conf.h"// 配置文件类
#include "my_memory.h"// 内存分配
#include "my_signal.h"
#include "global.h"
#include "log.h"
#include "zmqLogic.h"
#include "net/threadpool.h"// 线程池
#include "net/crc32.h"// crc32校验算法
#include "net/slogic.h"
#include "net/netglobal.h"

// main函数内部使用的函数的声明
static void FreeResource();

// 全局变量声明
size_t          g_argvneedmem; // 保存启动时argv参数所需要的内存大小
size_t          g_envneedmem; // 环境变量所占内存大小
int             g_os_argc;// argv参数个数
char **         g_os_argv;// 指向原始命令行参数数组，在main中赋值
char *          gp_envmem;// 指向自己分配的环境变量内存
int             g_daemonized;// 守护进程标记，是否启用（0不启用，1启用）
int             no_daemon;// 通过参数指定是否开启守护进程（优先级比配置表高）
CLogicSocket    g_socket;// 全局套接字对象
CThreadPool     g_threadpool;// 全局线程池对象
pid_t           g_pid;// 当前进程pid
pid_t           g_child_pid;// 子进程的pid
int             g_log;// 日志对象
int             g_process;// 进程类型（master或worker）
int             g_stopEvent;// 程序退出标志（0退出）
const char*     g_title;// 保存进程标题
// zmq对象声明
zmqLogic        g_zmq0;// net（主）连接master，PAIR→PAIR
zmqLogic        g_zmq1;// net（主）连接game，PAIR→PAIR
zmqLogic        g_zmq2;// net连接log（主），PUSH→PULL

int main(int argc, char *const *argv){
    int exitcode = 0;
    // 1.优先加载配置表
    CConfig *p_config = CConfig::GetInstance();
    if(p_config->Load("../all.conf") == false){
        Log_init("net_log");// 强行打开日志（输出到终端）
        Log_Error(0, "配置文件[%s]载入失败，退出!", "all.conf");
        exitcode = 2;
        goto lblexit;
    }

    // 2.初始化有必要的全局变量
    g_log = -1;// 日志尚未打开
    g_process = PROCESS_MASTER;// 标记此进程是master进程
    g_title = "myNet";// 先给，以防配置表读不出来
    g_stopEvent = 0;// 标记程序当前不退出
    g_pid = getpid();// 获取当前进程pid
    g_child_pid = -1;// 子进程给个默认值
    g_title = p_config->GetString("net_Title");
    g_daemonized = p_config->GetIntDefault("net_Daemon", 0);
    g_argvneedmem = 0;// 保存启动时argv参数所需要的内存大小
    g_envneedmem = 0;// 环境变量所占内存大小
    gp_envmem = NULL;// 指向自己分配的环境变量内存
    g_daemonized = 0;// 守护进程标记，是否启用（0不启用，1启用）
    no_daemon = 1;// 通过参数指定是否开启守护进程（优先级比配置表高）
    no_daemon = Init_args(argc, argv);// 处理main函数传递的参数（放置到新内存，为设置标题做准备）
    CMemory::GetInstance();// 调用一下但单例类（初始化）
    CCRC32::GetInstance();// 调用一下但单例类（初始化）

    // 3.初始化日志系统和程序标题
    Log_init("net_log");
    Setproctitle(g_title);// 设置进程标题

    // 4.初始化信号
    if(Init_signals() != 0){// 信号初始化
        exitcode = 1;
        goto lblexit;
    }

    // 5.是否以守护进程模式运行(no_daemon防止自动重启时发生嵌套)
    if(no_daemon == 1 && (g_daemonized == 1)){
        int cdaemonresult = Daemon();
        if(cdaemonresult == -1){// fork系统调用失败了
            exitcode = 1;
            goto lblexit;
        }
        if(cdaemonresult == 1){
            // 说明守护进程创建成功
            Log_Info(0, "成功创建并开启守护进程，守护进程已经在默默守护我们了！");
        }
    }

    // 6.监听套接字初始化
    if(g_socket.Initialize() == false)
    {
        exitcode = 1;
        goto lblexit;
    }

    // 7.ZMQ初始化
    if(Init_zmq() == false){
        exitcode = 1;
        goto lblexit;
    }

    // 8.进入工作循环（退出就说明程序结束了）
    Master_process_cycle();

    lblexit:
    Log_Info(0, "程序退出，燕子，我们还会再见吗！");
    FreeResource();// 释放资源
    return exitcode;
}

/**
 * 资源清理函数
 */
static void FreeResource(){
    if(gp_envmem){
        delete []gp_envmem;// 清理环境变量新位置的内存
        gp_envmem = NULL;
    }
    if(g_log != STDERR_FILENO && g_log != -1){        
        close(g_log); // 关闭日志文件
        g_log = -1; //标记下，防止被再次close吧        
    }
}

